﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Linq.Expressions;

namespace SimpleORM
{
    public abstract class Querybase : IQuerybase//AOPTarget, 
    {
        #region Properties

        public string Represent
        {
            get { return CommandContext.CommandText.ToString(); }
        }

        public ICommandContext CommandContext
        {
            get { return GetCommandContext(); }
            internal set { _CommandContext = value; }
        }

        public IProviderDatabase DataSource { get; private set; }
        public QuerySettings Settings { get; set; }

        #endregion

        #region Fields

        private ICommandContext _CommandContext;

        #endregion

        #region Ctor

        public Querybase(IProviderDatabase provider)
        {
            if (provider == null)
                throw new ArgumentNullException("provider");

            Settings = new QuerySettings();
            DataSource = provider;
        }

        #endregion

        #region Methods

        #region Methods - Execute

        public virtual object Execute()
        {
            IDCommand cmd = DataSource.BuildScript(CommandContext);

            cmd.Argument = CommandContext.ExecuteArgument;
            if (Settings.CommandTimeout != null)
                cmd.CommandTimeout = Settings.CommandTimeout.Value;

            return cmd.Execute();
        }

        public virtual object ExecuteInTransaction()
        {
            return Execute();
        }

        public virtual DataTable GetTable()
        {
            return DataSource.CreateDataReader().GetTable(Execute());
        }

        public virtual IList GetObjects(Type elementType)
        {
            return DataSource.CreateDataReader().
                GetObjects(Execute(), ObjectContext.Create(elementType));
        }

        #endregion

        #region Methods - Cache

        public void ClearCache()
        {
            //DbCache.Clear(DataSource.ConnectionString);
        }

        #endregion

        #region Methods - System

        public object Clone()
        {
            return null;
            //return QuerybaseAOP.Create(CloneInstance());
        }

        public void Dispose()
        {
            // base.Dispose();

            _CommandContext = null;
            Settings = null;
            DataSource = null;
        }

        #endregion

        #region Methods - Internal

        internal Querybase CloneInstance()
        {
            Querybase qc = this;
            qc.Settings = Settings;
            qc.DataSource = DataSource;
            qc.CommandContext = CommandContext.Clone() as ICommandContext;

            return qc;
        }

        #endregion

        #region Methods - Private

        private ICommandContext GetCommandContext()
        {
            if (_CommandContext == null)
                _CommandContext = DataSource.CreateScriptFactory();

            return _CommandContext;
        }

        #endregion

        #endregion
    }
    public partial class QueryContext : Querybase, IQueryContext
    {
        #region Properties

        public IQueryProvider Provider { get; private set; }
        public Type ElementType { get; private set; }
        public Expression Expression { get; private set; }

        #endregion

        #region Fields

        #endregion

        #region Ctor

        public QueryContext(IProviderDatabase provider)
            : base(provider)
        {
            Expression = Expression.Constant(this);
            Provider = new QueryContextProvider(this);
        }

        public QueryContext(IProviderDatabase provider, Type objectType)
            : this(provider)
        {
            ElementType = objectType;
        }

        public QueryContext(IProviderDatabase provider, Type objectType, Expression expression)
            : this(provider, objectType)
        {
            Expression = expression;
        }

        #endregion

        #region Methods

        #region Methods - Script

        public virtual void Compile()
        {
            ExpressionTranslater translater = DataSource.CreateExpressionTranslater();
            translater.Translate(Expression);

            CommandContext = translater.Result;
        }

        public virtual IQueryContext Combine(IQueryContext qc)
        {
            QueryContext qcInstance = (QueryContext)CloneInstance();

            if (qc != null)
                qcInstance.CommandContext =
                    (qcInstance.CommandContext as IScriptFactory).
                    Combine(qc.CommandContext as IScriptFactory);

            return qcInstance;
        }

        public virtual void Append(IQueryContext qc)
        {
            if (qc == null)
                return;

            (CommandContext as IScriptFactory).
                Append(qc.CommandContext as IScriptFactory);
        }

        #endregion

        #region Methods - System

        public IEnumerator GetEnumerator()
        {
            IEnumerable data = Provider.Execute<IEnumerable>(Expression);
            if (data == null)
                return null;

            return data.GetEnumerator();
        }

        public new void Dispose()
        {
            base.Dispose();

            Expression = null;
            ElementType = null;
        }

        #endregion

        #endregion
    }
    public class QueryContext<T> : QueryContext, IQueryContext<T>
    {
        #region Properties

        #endregion

        #region Ctor

        public QueryContext(IProviderDatabase provider)
            : base(provider, typeof(T))
        {
        }

        public QueryContext(IProviderDatabase provider, Expression expression)
            : base(provider, typeof(T), expression)
        {
        }

        #endregion

        #region Methods

        #region Methods - Execute

        public new virtual T Execute()
        {
            return (T)base.Execute();
        }

        public new virtual T ExecuteInTransaction()
        {
            return (T)base.ExecuteInTransaction();
        }

        public virtual List<T> GetObjects()
        {
            return base.GetObjects(ElementType) as List<T>;
        }

        #endregion

        #region Methods - Script

        public virtual IQueryContext<T> Combine(IQueryContext<T> qc)
        {
            return base.Combine(qc) as IQueryContext<T>;
        }

        #endregion

        #region Methods - System

        public new IEnumerator<T> GetEnumerator()
        {
            IEnumerable<T> data = Provider.Execute<IEnumerable<T>>(Expression);
            if (data == null)
                return null;

            return data.GetEnumerator();
        }

        #endregion

        #endregion

    }
}
